package com.link2room.blaster.facade.impl;

import com.link2room.blaster.dto.inMail.WebSocketDto;
import com.link2room.blaster.dto.innerUse.InmailBaseDto;
import com.link2room.blaster.dto.param.inMail.*;
import com.link2room.blaster.entity.InMailBaseEntity;
import com.link2room.blaster.entity.InMailDetailEntity;
import com.link2room.blaster.entity.InMailReceiveEntity;
import com.link2room.blaster.entity.InMailTypeEntity;
import com.link2room.blaster.facade.InMailFacadeService;
import com.link2room.blaster.service.IInmailService;
import com.link2room.blaster.service.IWebSocketSessionCacheService;
import com.link2room.blaster.service.InmailReceiverService;
import com.link2room.common.dto.param.base.BaseL2rParamDto;
import com.link2room.common.dto.result.base.AdminSessionDto;
import com.link2room.common.exception.SysException;
import com.link2room.common.jpa.criteria.CriteriaUtil;
import com.link2room.common.jpa.result.QueryResult;
import com.link2room.common.service.ICommonService;
import com.link2room.common.service.impl.Link2RoomApiBaseServiceImpl;
import com.link2room.common.util.ClassConverUtil;
import com.link2room.common.util.JacksonUtil;
import com.link2room.common.util.L2RAssert;
import com.link2room.enums.exception.BlasterSysExceptionEnum;
import org.apache.commons.collections.CollectionUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.integration.redis.util.RedisLockRegistry;
import org.springframework.stereotype.Component;

import javax.annotation.Resource;
import javax.websocket.Session;
import java.util.HashMap;
import java.util.List;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.Lock;
import java.util.stream.Collectors;

import static com.link2room.enums.exception.BlasterSysExceptionEnum.CREATE_INMAIL_TERROR;


/**
 * Created by LN on 2017/6/27.14:34
 */
@Component("inMailFacadeService")
public class InMailFacadeServiceImpl implements InMailFacadeService {
    @Resource
    private ICommonService commonService;
    @Resource
    private Link2RoomApiBaseServiceImpl link2RoomApiBaseService;
    @Resource
    private InmailReceiverService inmailReceiverService;
    @Resource
    private IWebSocketSessionCacheService webSocketSessionCacheService;
    @Resource
    private IInmailService inmailService;

    @Autowired
    private RedisLockRegistry inmailReadStaCountLock;
    @Autowired
    private RedisLockRegistry inmailDoneStaCountLock;

    @Override
    public void login(InMailConnectParamDto paramDto) throws Exception {
        String ticket = paramDto.getTicket();
        L2RAssert.stringBlank(ticket, "[ticket] is null.");
        BaseL2rParamDto adminSessionFind = new BaseL2rParamDto();
        adminSessionFind.setTicket(ticket);
        AdminSessionDto adminSession = link2RoomApiBaseService.getAdminSession(adminSessionFind);
        WebSocketDto webSocketDto = new WebSocketDto();
        webSocketDto.setSession(paramDto.getSession());
        webSocketDto.setTicket(ticket);
        webSocketDto.setUserCode(adminSession.getCode());
        webSocketSessionCacheService.cacheSession(adminSession.getCode(), webSocketDto);
    }

    @Override
    public void logout(InMailConnectParamDto paramDto) {
        Session session = paramDto.getSession();
        if (session == null) {
            return;
        }
        webSocketSessionCacheService.remove(session.getId());
    }

    @Override
    public void sendMessage(SendMessageParamDto paramDto) {
        String userCode = paramDto.getUserCode();
        L2RAssert.stringBlank(userCode, "[userCode] is null.");
        String message = paramDto.getMessage();
        L2RAssert.stringBlank(message, "[message] is null.");
        List<WebSocketDto> webSocketDtos = webSocketSessionCacheService.getSession(userCode);
        if (CollectionUtils.isNotEmpty(webSocketDtos)) {
            webSocketDtos.stream().forEach(webSocketDto -> webSocketDto.sendMessage(message));
        }

    }

    @Override
    public InmailBaseDto createInmail(InmailCreateParamDto paramDto) throws Exception {
        String lhotelGroupCode = paramDto.getLhotelGroupCode();
        L2RAssert.stringBlank(lhotelGroupCode, "[lhotelGroupCode] is null .");
        String lhotelCode = paramDto.getLhotelCode();
        L2RAssert.stringBlank(lhotelCode, "[lhotelCode] is null .");
        InmailBaseDto inmailBaseDto = paramDto.getInmalBaseDto();
        L2RAssert.isTrue(inmailBaseDto == null, new SysException(CREATE_INMAIL_TERROR, "[inmailBaseDto] is null ."));
        String type = inmailBaseDto.getType();
        L2RAssert.isTrue(!hasType(type), new SysException(CREATE_INMAIL_TERROR, "[inmailType] is not exit."));
        String title = inmailBaseDto.getTitle();
        L2RAssert.stringBlank(title, "[title] is null.");
        String message = inmailBaseDto.getMessage();
        L2RAssert.stringBlank(message, "[message] is null.");
        //inmail ID generation
        String inmailId = inmailService.generateInmailId();
        //match receiver
        List<String> receiverCode = inmailReceiverService.findReceiver(lhotelGroupCode, lhotelCode, type);
        if (CollectionUtils.isEmpty(receiverCode)) {
            return null;
        }
        inmailService.createInmail(inmailBaseDto, inmailId, receiverCode);
        inmailBaseDto.setInmailId(inmailId);
        return inmailBaseDto;
    }

    @Override
    public void sendInmailId(SendInmailIdParamDto paramDto) throws Exception {
        InmailBaseDto inmailBase = paramDto.getInmailBaseDto();
        L2RAssert.isTrue(inmailBase == null, new SysException(BlasterSysExceptionEnum.INMAIL_TYPE_ERROR,"inmail base is null."));
        List<String> receiver = inmailService.findReceiver(inmailBase.getInmailId());
        if (CollectionUtils.isNotEmpty(receiver)) {
            receiver.forEach(userCode -> sendMessage(userCode, inmailBase));
        }
    }

    @Override
    public QueryResult inmailList(InmailBaseParamDto paramDto) throws Exception {

        String lhotelGroupCode = paramDto.getLhotelGroupCode();
        L2RAssert.stringBlank(lhotelGroupCode, "[lhotelGroupCode] is null.");

        AdminSessionDto session = link2RoomApiBaseService.getAdminSession(paramDto);
        String userCode = session.getCode();
        InMailReceiveEntity findInamilId = new InMailReceiveEntity();
        findInamilId.setHasRead(paramDto.getHasRead());
        findInamilId.setHasDone(paramDto.getHasDone());
        findInamilId.setReceiverCode(userCode);

        HashMap<String, String> orderBy = new HashMap<>();
        orderBy.put(InMailBaseEntity.BaseEntity_.id.toString(),CriteriaUtil.DESC );
        QueryResult<InMailReceiveEntity> qres = commonService.findAllEQ(InMailReceiveEntity.class, findInamilId, paramDto.getCurrentPage(), paramDto.getPageSize(), orderBy, null);


        if (qres == null || CollectionUtils.isEmpty(qres.getResultlist())) {
            return qres;
        } else {
            List<String> inmailIds = qres.getResultlist().stream()
                    .map(InMailReceiveEntity::getMailId)
                    .collect(Collectors.toList());
            if(CollectionUtils.isNotEmpty(inmailIds)){
                QueryResult res = new QueryResult();
                res.setTotalrecord(qres.getTotalrecord());
                res.setResultlist(inmailService.findInmailBaseByInmailId(inmailIds));
                res.setCurrentPage(qres.getCurrentPage());
                return res;
            }else {
                QueryResult res = new QueryResult();
                res.setTotalrecord(0);
                res.setCurrentPage(0);
                return res;
            }
        }
    }

    @Override
    public InmailBaseDto inmailDetail(InmailBaseParamDto paramDto) throws Exception {
        String inmailId = paramDto.getInmailId();

        InMailBaseEntity findBase = new InMailBaseEntity();
        findBase.setMailId(inmailId);
        InMailBaseEntity baseEntity = commonService.findOneEQ(InMailBaseEntity.class, findBase);
        if(baseEntity == null ){
            return null;
        }
        InmailBaseDto baseDto = ClassConverUtil.copyProperties(baseEntity, InmailBaseDto.class);

        InMailDetailEntity findDetail = new InMailDetailEntity();
        findDetail.setMailId(inmailId);
        InMailDetailEntity detailEntity = commonService.findOneEQ(InMailDetailEntity.class, findDetail);
        if(detailEntity!= null){
            baseDto.setMessage(detailEntity.getMessage());
        }
        return baseDto;
    }

    @Override
    public long countInmail(InmailBaseParamDto paramDto) throws Exception {
        String hasRead = paramDto.getHasRead();
        String hasDone = paramDto.getHasDone();
        AdminSessionDto adminUser = link2RoomApiBaseService.getAdminSession(paramDto);
        String userCode = adminUser.getCode();

        return inmailService.countInmail(userCode,hasRead,hasDone);
    }

    @Override
    public void inmailRead(InmailBaseParamDto paramDto) throws Exception {
        String inmailId = paramDto.getInmailId();
        L2RAssert.stringBlank(inmailId,"[inmailId] is null.");
        AdminSessionDto adminUser = link2RoomApiBaseService.getAdminSession(paramDto);
        String userCode = adminUser.getCode();
        inmailService.readInmail(userCode,inmailId);
        Lock lock = inmailReadStaCountLock.obtain(inmailId);
        if(lock.tryLock(1, TimeUnit.SECONDS)){
            try {
                if(inmailService.hasReadAll(inmailId)){
                    inmailService.readBaseInamil(inmailId);
                }
            }finally {
                lock.unlock();
            }
        }
    }

    @Override
    public void inmailDone(InmailBaseParamDto paramDto) throws Exception {
        String inmailId = paramDto.getInmailId();
        L2RAssert.stringBlank(inmailId,"[inmailId] is null.");
        AdminSessionDto adminUser = link2RoomApiBaseService.getAdminSession(paramDto);
        String userCode = adminUser.getCode();
        inmailService.doInmail(userCode,inmailId);
        Lock lock = inmailDoneStaCountLock.obtain(inmailId);
        if(lock.tryLock(1, TimeUnit.SECONDS)){
            try {
                if(inmailService.hasDoneAll(inmailId)){
                    inmailService.doBaseInamil(inmailId);
                }
            }finally {
                lock.unlock();
            }
        }
    }

    private void sendMessage(String userCode, InmailBaseDto inmail) {

        List<WebSocketDto> webSocketDtos = webSocketSessionCacheService.getSession(userCode);
        if (CollectionUtils.isNotEmpty(webSocketDtos)) {
            String message = JacksonUtil.beanToJson(inmail);
            webSocketDtos.stream().forEach(webSocketDto -> webSocketDto.sendMessage(message));
        }
    }

    private boolean hasType(String inmailType) throws Exception {
        InMailTypeEntity find = new InMailTypeEntity();
        find.setCode(inmailType);
        long count = commonService.getCountAllEQ(InMailTypeEntity.class, find, null);
        return count > 0;
    }
}
